package com.wjf.gateway.config;

import com.wjf.common.utils.JwtUtils;
import com.wjf.common.utils.ThreadLocalUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(AuthenticationFilter.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    // 白名单路径
    private static final List<String> WHITE_LIST = new ArrayList<>() {
        {
            add("/users/login");
            add("/users/register");
            // 可以继续添加其他白名单路径
        }
    };

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        log.info("收到请求，路径: {}", path);

        // 如果是白名单路径，直接放行
        if (isWhitePath(path)) {
            log.debug("路径 {} 在白名单中，直接放行", path);
            return chain.filter(exchange);
        }

        // 从请求头获取token
        String token = request.getHeaders().getFirst("Authorization");
        log.info("从请求头获取到的原始token: {}", token);

        // 验证token
        if (!StringUtils.hasText(token)) {
            log.warn("请求路径 {} 未提供token", path);
            return unauthorized(exchange);
        }

        try {
            // 从Redis验证token
            String redisToken = redisTemplate.opsForValue().get(token);
            log.info("Redis中的token: {}", redisToken);

            if (redisToken == null || !token.equals(redisToken)) {
                log.warn("Redis中未找到匹配的token");
                return unauthorized(exchange);
            }

            // 解析JWT token
            Map<String, Object> claims = JwtUtils.parseToken(token);
            if (claims == null) {
                log.warn("JWT token解析失败");
                return unauthorized(exchange);
            }

            // token有效，将用户信息传递下去
            ServerHttpRequest mutableReq = request.mutate()
                    .header("token", token) // 传递token给下游服务
                    .build();

            ServerWebExchange mutableExchange = exchange.mutate()
                    .request(mutableReq)
                    .build();

            return chain.filter(mutableExchange);
        } catch (Exception e) {
            log.error("处理token时发生错误: {}", e.getMessage(), e);
            return unauthorized(exchange);
        }
    }

    private Mono<Void> unauthorized(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");

        String body = "{\"code\": 401, \"message\": \"未登录或token已过期\"}";
        DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));

        log.warn("未授权的访问请求");
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -100;
    }

    private boolean isWhitePath(String path) {
        return WHITE_LIST.stream().anyMatch(path::startsWith);
    }
}